package com.qijian.flowable.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Sets;
import com.qijian.common.annotation.PlugAnn;
import com.qijian.common.annotation.PlugParam;
import com.qijian.common.core.domain.entity.FlowableBack;
import com.qijian.common.enums.ApprovalDescriptionEnum;
import com.qijian.common.enums.FlowableKeyEnum;
import com.qijian.common.utils.DateUtils;
import com.qijian.common.utils.SecurityUtils;
import com.qijian.common.utils.StringUtils;
import com.qijian.common.utils.*;
import com.qijian.common.utils.mybatis.WhereEntityTool;
import com.qijian.flowable.common.cmd.*;
import com.qijian.flowable.common.enums.ButtonsEnum;
import com.qijian.flowable.common.enums.CommentTypeEnum;
import com.qijian.flowable.constant.FlowableConstant;
import com.qijian.flowable.domain.FlowableRecord;
import com.qijian.flowable.domain.FlowableRecordPower;
import com.qijian.flowable.domain.query.FlowableRecordPowerQuery;
import com.qijian.flowable.domain.vo.*;
import com.qijian.flowable.mapper.FlowableCommonMapper;
import com.qijian.flowable.service.IFlowableRecordPowerService;
import com.qijian.flowable.service.IFlowableRecordService;
import com.qijian.flowable.service.IFlowableTaskService;
import com.qijian.flowable.service.IPermissionService;
import com.qijian.flowable.util.ExpressionCmd;
import com.qijian.flowable.util.FlowableUtils;
import com.qijian.tool.domain.Msg;
import com.qijian.tool.service.IMsgService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.User;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 庄金明
 * @date 2020年3月23日
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class FlowableTaskServiceImpl implements IFlowableTaskService {
    private final IdentityService identityService;
    private final RepositoryService repositoryService;
    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final HistoryService historyService;
    private final IPermissionService permissionService;
    private final ManagementService managementService;
    private final FormService formService;
    private final IFlowableRecordService flowableRecordService;
    private final IFlowableRecordPowerService flowableRecordPowerService;
    private final FlowableCommonMapper flowableCommonRepository;

    private final ProcessEngine processEngine;

    private final IMsgService msgService;

    @Override
    public TaskResponse getTask(String taskId) {
        String userId = SecurityUtils.getUserId().toString();
        HistoricTaskInstance taskHis = permissionService.validateReadPermissionOnTask(taskId, userId, true, true);
        TaskResponse rep;
        ProcessDefinition processDefinition = null;
        String formKey = null;
        Object renderedTaskForm = null;
        HistoricTaskInstance parentTask = null;
        if (StringUtils.isNotEmpty(taskHis.getProcessDefinitionId())) {
            processDefinition = repositoryService.getProcessDefinition(taskHis.getProcessDefinitionId());
            formKey = formService.getTaskFormKey(processDefinition.getId(), taskHis.getTaskDefinitionKey());
            if (taskHis.getEndTime() == null && formKey != null && formKey.length() > 0) {
                renderedTaskForm = formService.getRenderedTaskForm(taskId);
            }
        }
        if (StringUtils.isNotEmpty(taskHis.getParentTaskId())) {
            parentTask =
                    historyService.createHistoricTaskInstanceQuery().taskId(taskHis.getParentTaskId()).singleResult();
        }
        rep = new TaskResponse(taskHis, processDefinition, parentTask, null);
        rep.setFormKey(formKey);
        rep.setRenderedTaskForm(renderedTaskForm);

        fillPermissionInformation(rep, taskHis, userId);
        // Populate the people
        populateAssignee(taskHis, rep);
        rep.setInvolvedPeople(getInvolvedUsers(taskId));

        Task task;
        if (taskHis.getEndTime() == null) {
            task = taskService.createTaskQuery().taskId(taskId).singleResult();
            rep.setSuspended(task.isSuspended());
            rep.setDelegationState(task.getDelegationState());
        }
        rep.setOwnerName(this.getUserName(taskHis.getOwner()));
        rep.setAssigneeName(this.getUserName(taskHis.getAssignee()));
        return rep;
    }

    @Override
    public List<TaskResponse> getSubTasks(String taskId) {
        String userId = SecurityUtils.getUserId().toString();
        HistoricTaskInstance parentTask = permissionService.validateReadPermissionOnTask(taskId, userId, true, true);
        List<Task> subTasks = this.taskService.getSubTasks(taskId);
        List<TaskResponse> subTasksRepresentations = new ArrayList<>(subTasks.size());
        for (Task subTask : subTasks) {
            TaskResponse representation = new TaskResponse(subTask, parentTask);
            fillPermissionInformation(representation, subTask, userId);
            populateAssignee(subTask, representation);
            representation.setInvolvedPeople(getInvolvedUsers(subTask.getId()));
            subTasksRepresentations.add(representation);
        }
        return subTasksRepresentations;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TaskResponse updateTask(TaskUpdateVo vo) {
        String userId = SecurityUtils.getUserId().toString();
        permissionService.validateReadPermissionOnTask(vo.getId(), userId, false, false);
        Task task = getTaskNotNull(vo.getId());
        task.setName(vo.getName());
        task.setDescription(vo.getDescription());
        task.setAssignee(vo.getAssignee());
        task.setOwner(vo.getOwner());
        task.setDueDate(vo.getDueDate());
        task.setPriority(vo.getPriority());
        task.setCategory(vo.getCategory());
        taskService.saveTask(task);
        return new TaskResponse(task);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String assignee = vo.getUserId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = permissionService.validateAssignPermissionOnTask(taskId, userId, assignee);
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.ZB, vo.getMessage());
        handMsg(task, vo);
        taskService.setAssignee(task.getId(), assignee);
        // 暂时转办人员不作为参与者
        // String oldAssignee = task.getAssignee();
        // // If the old assignee user wasn't part of the involved users yet, make it so
        // addIdentiyLinkForUser(task, oldAssignee, IdentityLinkType.PARTICIPANT);
        // // If the current user wasn't part of the involved users yet, make it so
        // addIdentiyLinkForUser(task, userId, IdentityLinkType.PARTICIPANT);
    }

    @Override
    public void resolvedTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        this.addComment(taskId, vo.getProcessInstanceId(), userId, CommentTypeEnum.WC, vo.getMessage());
        handMsg(task, vo);
        taskService.resolveTask(taskId);
    }

    @Override
    public void finishForward(TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        this.addCommentZF(taskId, vo.getProcessInstanceId(), userId, CommentTypeEnum.PZ, vo.getMessage());
        List<Msg> zf0 = msgService.list(new LambdaQueryWrapper<Msg>().eq(Msg::getRemark, "ZF_0").eq(Msg::getUserId, userId).eq(Msg::getTaskId, taskId));
        if (CollectionUtil.isNotEmpty(zf0)) {
            zf0.stream().forEach(e->{
                e.setRemark("ZF_1");
            });
            msgService.updateBatchById(zf0);
        }
    }


    public void handMsg(Task task, TaskVo vo) {
        Map<String, Object> variables = taskService.getVariables(vo.getTaskId());
        String userName = variables.get("userName").toString();
        String dateTime = variables.get("createTime").toString();
        String title = variables.get("title").toString();
        String type = variables.get("type").toString();
        Msg msg1 = new Msg();
        msg1.setTitle(title);
        variables.forEach((k, v) -> {
            if (k.indexOf("input_") > -1) {
                msg1.setSourceId(v.toString());
                msg1.setUrl(FlowableKeyEnum.flowToValues(type));
            }
        });
        msg1.setSourceType(type);
        msg1.setContent(title);
        msg1.setOriginCreateDateTime(DateUtils.strToDate(dateTime, DatePattern.NORM_DATETIME_PATTERN));
        msg1.setCreateByName(userName);
        msg1.setTaskId(vo.getTaskId());
        msg1.setProcessInstanceId(task.getProcessInstanceId());
        msg1.setUserId(Long.parseLong(vo.getUserId()));
        List<Msg> msgs = new ArrayList<>();
        msgs.add(msg1);
        msgService.saveBatch(msgs);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void involveUser(String taskId, String involveUserId) {
        Task task = getTaskNotNull(taskId);
        String userId = SecurityUtils.getUserId().toString();
        permissionService.validateReadPermissionOnTask(task.getId(), userId, false, false);
        if (involveUserId != null && involveUserId.length() > 0) {
            taskService.addUserIdentityLink(taskId, involveUserId, IdentityLinkType.PARTICIPANT);
        } else {
            throw new FlowableException("User id is required");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeInvolvedUser(String taskId, String involveUserId) {
        Task task = getTaskNotNull(taskId);
        String userId = SecurityUtils.getUserId().toString();
        permissionService.validateReadPermissionOnTask(task.getId(), userId, false, false);
        if (involveUserId != null && involveUserId.length() > 0) {
            taskService.deleteUserIdentityLink(taskId, involveUserId, IdentityLinkType.PARTICIPANT);
        } else {
            throw new FlowableException("User id is required");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void claimTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        TaskInfo task = permissionService.validateReadPermissionOnTask2(taskId, userId, false, false);
        if (task.getAssignee() != null && task.getAssignee().length() > 0) {
            throw new FlowableException("该任务已分配");
        }
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.RL, vo.getMessage());
        taskService.claim(taskId, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unclaimTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = this.getTaskNotNull(taskId);
        if (StringUtils.isBlank(task.getAssignee())) {
            throw new FlowableException("任务未先签收");
        }
        if (!userId.equals(task.getAssignee())) {
            throw new FlowableException("签收人错误");
        }
        if (FlowableConstant.CATEGORY_TO_READ.equals(task.getCategory())) {
            throw new FlowableException("用户无法签收阅读任务");
        }
        if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) {
            throw new FlowableException("初始化任务无需签收");
        }


        // 判断是否可有可签收人员
        List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(task.getId());
        boolean isCheck = true;
        for (IdentityLink identityLink : identityLinks) {
            if (StringUtils.isNotEmpty(identityLink.getGroupId())) {
                isCheck = false;
            }
        }
        if (isCheck) {
            throw new FlowableException("指定审批人员，无法取消签收！");
        }
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.QXRL, vo.getMessage());
        taskService.unclaim(taskId);
        // 判断是否是协办取消认领
        if (permissionService.isTaskPending(task)) {
            taskService.resolveTask(taskId, null);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addIdentiyLinkForUser(Task task, String userId, String linkType) {
        List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(task.getId());
        boolean isOldUserInvolved = false;
        for (IdentityLink identityLink : identityLinks) {
            isOldUserInvolved =
                    userId.equals(identityLink.getUserId()) && (identityLink.getType().equals(IdentityLinkType.PARTICIPANT) || identityLink.getType().equals(IdentityLinkType.CANDIDATE));
            if (isOldUserInvolved) {
                break;
            }
        }
        if (!isOldUserInvolved) {
            taskService.addUserIdentityLink(task.getId(), userId, linkType);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegateTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String delegater = vo.getUserId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = permissionService.validateDelegatePermissionOnTask(taskId, userId, delegater);
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.ZF, vo.getMessage()+"["+vo.getNickName()+"]");
        taskService.delegateTask(task.getId(), delegater);
        FlowableRecord one = flowableRecordService.getOne(new LambdaQueryWrapper<FlowableRecord>().eq(FlowableRecord::getProcessInstanceId, vo.getProcessInstanceId()));
        if (one != null) {
            List<Msg> msgs = new ArrayList<>();
            Msg msg = new Msg();
            msg.setTitle(one.getRecordName());
            msg.setSourceId(one.getSourceId().toString());
            msg.setSourceType(one.getBusinessKey());
            msg.setContent("您收到一条转发消息");
            msg.setUrl(FlowableKeyEnum.flowToValues(one.getBusinessKey()) + "?processInstanceId=" + vo.getProcessInstanceId());
            msg.setOriginCreateDateTime(one.getCreateTime());
            msg.setCreateByName(one.getCreateBy());
            msg.setTaskId(vo.getTaskId());
            msg.setProcessInstanceId(vo.getProcessInstanceId());
            msg.setUserId(Long.parseLong(vo.getUserId()));
            msgs.add(msg);
            msgService.saveBatch(msgs, false, false);
        }

        // 暂时委派人员不作为参与者
        // String oldAssignee = task.getAssignee();
        // // If the old assignee user wasn't part of the involved users yet, make it so
        // addIdentiyLinkForUser(task, oldAssignee, IdentityLinkType.PARTICIPANT);
        // // If the current user wasn't part of the involved users yet, make it so
          addIdentiyLinkForUser(task, userId, IdentityLinkType.PARTICIPANT);
    }

    @Override
    @Transactional
    public void forwardTask(List<TaskVo> taskRequests) {
        String userId = SecurityUtils.getUserId().toString();
        if (CollectionUtil.isNotEmpty(taskRequests)) {
            FlowableRecord one = flowableRecordService.getOne(new LambdaQueryWrapper<FlowableRecord>().eq(FlowableRecord::getProcessInstanceId, taskRequests.get(0).getProcessInstanceId()));
            taskRequests.stream().forEach(vo -> {
                Task task = permissionService.validateAssignPermissionOnTaskZF(vo.getTaskId(), userId, vo.getUserId(), vo.getProcessInstanceId());
                this.addComment(vo.getTaskId(), task.getProcessInstanceId(), userId, CommentTypeEnum.ZF,  "[" + vo.getUserId() + "_" + vo.getNickName() + "]:" +vo.getMessage());
                addRecordPower(null, vo.getProcessInstanceId(), flowableRecordPowerService, vo.getUserId());
                if (one != null) {
                    List<Msg> msgs = new ArrayList<>();
                    Msg msg = new Msg();
                    msg.setTitle("(转发)"+one.getRecordName());
                    msg.setSourceId(one.getSourceId().toString());
                    msg.setSourceType(one.getBusinessKey());
                    msg.setContent("您收到一条转发消息");
                    msg.setUrl(FlowableKeyEnum.flowToValues(one.getBusinessKey()) + "?processInstanceId=" + vo.getProcessInstanceId());
                    msg.setOriginCreateDateTime(one.getCreateTime());
                    msg.setCreateByName(one.getCreateBy());
                    msg.setTaskId(vo.getTaskId());
                    msg.setProcessInstanceId(vo.getProcessInstanceId());
                    msg.setUserId(Long.parseLong(vo.getUserId()));
                    msg.setRemark("ZF_0");
                    msg.setMsgLevel(task.getName());
                    msgs.add(msg);
                    msgService.saveBatch(msgs, false, false);
                }
            });
        }

    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeTask(TaskVo vo, String currUserId) {
        String taskId = vo.getTaskId();
        currUserId = StringUtils.isBlank(currUserId) ? SecurityUtils.getUserId().toString() : currUserId;

        Task task = getTaskNotNull(taskId);

        Map<String, Object> completeVariables = handleTask(vo, task, currUserId, null, true, 0);
        if (task != null && task.getName().equals("库存确认")) {
            Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId());
            if (ObjectUtils.isNotNull(variables.get("kcqrs"))) {
                String kcqrs = variables.get("kcqrs").toString();
                kcqrs = task.getAssignee() + "," + kcqrs;
                variables.put("kcqrs", kcqrs);
            } else {
                variables.put("kcqrs", task.getAssignee());
            }
            runtimeService.setVariables(task.getProcessInstanceId(), variables);
        }

        // 执行
        this.addComment(taskId, task.getProcessInstanceId(), currUserId,
                FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey()) ? CommentTypeEnum.CXTJ :
                        CommentTypeEnum.WC, vo.getMessage());
        // 处理抄送
        if (ObjectUtil.isNotEmpty(vo.getCcToVos())) {
            managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(),
                    currUserId, vo.getCcToVos()));

            // 添加流程记录查看权限
            addRecordPower(vo.getCcToVos() != null ? Arrays.asList(vo.getCcToVos()) : null, task.getProcessInstanceId(), flowableRecordPowerService, null);
            Map<String, Object> variables = new HashMap<>();
            variables.put("cc", StringUtils.join(Arrays.stream(vo.getCcToVos()).map(e -> Long.parseLong(e.getUserId())).collect(Collectors.toList()), ","));
            runtimeService.setVariables(task.getProcessInstanceId(), variables);
        }

        if (task.getAssignee() == null || !task.getAssignee().equals(currUserId)) {
            taskService.setAssignee(taskId, currUserId);
        }
        // 判断是否是协办完成还是正常流转
        if (permissionService.isTaskPending(task)) {
            taskService.resolveTask(taskId, completeVariables);
            // 如果当前执行人是任务所有人，直接完成任务
            if (currUserId.equals(task.getOwner())) {
                taskService.complete(taskId, null);
            }
        } else {
            taskService.complete(taskId, completeVariables);
        }

        // 下一审批人是否自己
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        if (CollectionUtil.isNotEmpty(tasks) && !tasks.get(0).getName().equals("采购定责") && !tasks.get(0).getName().equals("流程挂起")) {
            List<HistoricIdentityLink> taskIdentityLinks = historyService.getHistoricIdentityLinksForTask(tasks.get(0).getId());
            if (taskIdentityLinks.size() == 1 && StringUtils.isNotBlank(taskIdentityLinks.get(0).getUserId()) && taskIdentityLinks.get(0).getUserId().equals(currUserId)) {
                // 当前提交人与后置审批人一致
                vo.setTaskId(tasks.get(0).getId());
                vo.setMessage("自动审核");

                // 认领
                this.addComment(vo.getTaskId(), task.getProcessInstanceId(), currUserId, CommentTypeEnum.RL, "自动认领");
                taskService.claim(vo.getTaskId(), currUserId);

                // 完成
                completeTask(vo, currUserId);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeTaskSystem(TaskVo vo, int jobLevel) {
        String taskId = vo.getTaskId();
        String currUserId = vo.getUserId();

        Boolean isCheck = false;

        String checkFiled = null;
        Task task = getTaskNotNull(taskId);
        Map<String, Object> completeVariables = handleTask(vo, task, currUserId, checkFiled, false, jobLevel);
        // 执行
        this.addComment(taskId, task.getProcessInstanceId(), currUserId,
                FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey()) ? CommentTypeEnum.CXTJ :
                        CommentTypeEnum.WC, vo.getMessage());
        // 处理抄送
        if (ObjectUtil.isNotEmpty(vo.getCcToVos())) {
            managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(),
                    currUserId, vo.getCcToVos()));

            // 添加流程记录查看权限
            addRecordPower(vo.getCcToVos() != null ? Arrays.asList(vo.getCcToVos()) : null, task.getProcessInstanceId(), flowableRecordPowerService, null);
        }

        if (task.getAssignee() == null || !task.getAssignee().equals(currUserId)) {
            taskService.setAssignee(taskId, currUserId);
        }
        // 判断是否是协办完成还是正常流转
        if (permissionService.isTaskPending(task)) {
            taskService.resolveTask(taskId, completeVariables);
            // 如果当前执行人是任务所有人，直接完成任务
            if (currUserId.equals(task.getOwner())) {
                taskService.complete(taskId, completeVariables);
            }
        } else {
            taskService.complete(taskId, completeVariables);
        }

        // 下一审批人是否自己
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        if (CollectionUtil.isNotEmpty(tasks)) {
            List<HistoricIdentityLink> taskIdentityLinks = historyService.getHistoricIdentityLinksForTask(tasks.get(0).getId());
            if (taskIdentityLinks.size() == 1 && taskIdentityLinks.get(0).getUserId().equals(currUserId)) {
                // 当前提交人与后置审批人一致
                vo.setTaskId(tasks.get(0).getId());
                vo.setMessage("自动审核");

                // 认领
                this.addComment(vo.getTaskId(), task.getProcessInstanceId(), currUserId, CommentTypeEnum.RL, "自动认领");
                taskService.claim(vo.getTaskId(), currUserId);

                // 完成
                completeTaskSystem(vo, jobLevel);
            }
        }
    }

    private Map<String, Object> handleTask(TaskVo vo, Task task, String currUserId, String checkFiled, Boolean isFrom, int jobLevel) {
        Map<String, Object> completeVariables = null;
        if (!permissionService.isTaskOwnerOrAssignee(currUserId, task)) {
            if (StringUtils.isEmpty(task.getScopeType()) && !permissionService.validateIfUserIsInitiatorAndCanCompleteTask(currUserId, task)) {
                throw new FlowableException("请先认领！");
            }
        }

        if (CollectionUtil.isNotEmpty(vo.getValues())) {
            completeVariables = vo.getValues();
            // 允许任务表单修改流程表单场景 begin
            // 与前端约定：流程表单变量名为 processInstanceFormData，且只有流程表单startFormKey=taskFormKey时才允许修改该变量的值，防止恶意节点修改流程表单内容
            if (isFrom && completeVariables.containsKey(FlowableConstant.PROCESS_INSTANCE_FORM_DATA)) {
                String startFormKey = formService.getStartFormKey(task.getProcessDefinitionId());
                String taskFormKey = formService.getTaskFormKey(task.getProcessDefinitionId(),
                        task.getTaskDefinitionKey());
                boolean modifyProcessInstanceFormData =
                        StringUtils.isNotBlank(startFormKey) && StringUtils.isNotBlank(taskFormKey) && taskFormKey.split("\\.")[0].contains(startFormKey.split("\\.")[0]);
                if (!modifyProcessInstanceFormData) {
                    throw new FlowableException("无权限修改表单");
                }
            }
            // 允许任务表单修改流程表单场景 end

            // 非会签用户节点，默认设置流程变量 __taskDefinitionKey__=currUserId，用于存储该节点执行人，且以最近的执行人为准
            UserTask userTask = (UserTask) FlowableUtils.getFlowElement(repositoryService,
                    task.getProcessDefinitionId(), task.getTaskDefinitionKey());
            if (userTask != null && !userTask.hasMultiInstanceLoopCharacteristics()) {
                completeVariables.put("__" + task.getTaskDefinitionKey() + "__", currUserId);
            }
        } else {
            // 非会签用户节点，默认设置流程变量 __taskDefinitionKey__=currUserId，用于存储该节点执行人，且以最近的执行人为准
            UserTask userTask = (UserTask) FlowableUtils.getFlowElement(repositoryService,
                    task.getProcessDefinitionId(), task.getTaskDefinitionKey());
            if (userTask != null && !userTask.hasMultiInstanceLoopCharacteristics()) {
                completeVariables = new HashMap<>(1);
                completeVariables.put("__" + task.getTaskDefinitionKey() + "__", currUserId);
            }
        }

        Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId());
        Boolean flag = variables.get("isUse") == null ? false : (Boolean) variables.get("isUse");
        if (completeVariables == null && flag) {
            completeVariables = new HashMap<>();
            completeVariables.put("jobLevel", jobLevel);
            // 查找下一节点是否UEL表达式用户
            ProcessDefinition pde = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
            BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
            for (Process process : bpmnModel.getProcesses()) {
                Map<String, FlowElement> map = process.getFlowElementMap();
                for (String key : map.keySet()) {
                    FlowElement value = map.get(key);
                    if (value instanceof UserTask && task.getTaskDefinitionKey().equals(key)) {
                        List<SequenceFlow> userTasks = ((UserTask) value).getOutgoingFlows();
                        if (CollectionUtil.isNotEmpty(userTasks)) {
                            FlowElement target = userTasks.get(0).getTargetFlowElement();
                            if (target instanceof UserTask) {
                                UserTask userTask = (UserTask) target;
                                if (StringUtils.isNotBlank(userTask.getAssignee()) && StringUtils.checkUEL(userTask.getAssignee())) {
                                    // 填充uel表达式
                                    String[] args = userTask.getId().split("___");
                                    if (args.length > 1) {
                                        Long parent = null;
                                        FlowableRecord record = flowableRecordService.getById(task.getProcessInstanceId());
                                        Map startVariables = JSONUtil.toBean(record.getBusinessValues(), Map.class);
                                        if ("select_user".equals(args[1])) {
                                            String userName = (String) startVariables.get(args[2]);
                                            if (StringUtils.isBlank(userName)) {
                                                throw new FlowableException("用户信息错误！");
                                            }
                                            Long execUserId = flowableCommonRepository.selectuserName(userName);
                                            parent = execUserId;
                                            completeVariables.put(args[2], execUserId.toString());
                                        } else if ("select_dept".equals(args[1])) {
                                            String deptName = (String) startVariables.get(args[2]);
                                            if (StringUtils.isBlank(deptName)) {
                                                throw new FlowableException("部门信息错误！");
                                            }
                                            Long leaderUserId = flowableCommonRepository.selectUserDeptLeader(deptName);
                                            if (leaderUserId == null) {
                                                throw new FlowableException("部门负责人未设置！");
                                            }
                                            parent = leaderUserId;
                                            completeVariables.put(args[2], leaderUserId.toString());
                                        } else {
                                            parent = getLeader(completeVariables, args, parent, startVariables, flowableCommonRepository);
                                        }

                                        if (parent != null) {
//                                                Msg msg = new Msg();
//                                                msg.setMsgTitle("流程待办");
//                                                msg.setMsgContent("您有一条" + record.getRecordName() + "待处理，请及时查看！");
//                                                msg.setUserId(parent);
//                                                msg.setFlowdType("FlowableWait");
//                                                msg.setFlowdId(record.getProcessInstanceId());
//                                                msgService.create(msg);
//                                                // 推送消息
//                                                usersGetuiService.pushMsg(Collections.singleton(msg.getUserId()), msg.getMsgTitle(), msg.getMsgContent());
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        if (CollectionUtil.isNotEmpty(completeVariables)) {
            Boolean isCheck = (Boolean) completeVariables.get("isCheck");
            checkFiled = (String) completeVariables.get("checkFiled");
            if (isCheck == null || !isCheck) {
                completeVariables.put("jobLevel", jobLevel);
                // 查找下一节点是否UEL表达式用户
                ProcessDefinition pde = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
                BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
                for (Process process : bpmnModel.getProcesses()) {
                    Map<String, FlowElement> map = process.getFlowElementMap();
                    for (String key : map.keySet()) {
                        FlowElement value = map.get(key);
                        if (value instanceof UserTask && task.getTaskDefinitionKey().equals(key)) {
                            List<SequenceFlow> userTasks = ((UserTask) value).getOutgoingFlows();
                            FlowElement target = null;
                            if (CollectionUtil.isNotEmpty(userTasks)) {
                                target = userTasks.get(0).getTargetFlowElement();
                                if (target instanceof UserTask) {
                                    UserTask userTask = (UserTask) target;
                                    if (StringUtils.isNotBlank(userTask.getAssignee()) && StringUtils.checkUEL(userTask.getAssignee())) {
                                        // 填充uel表达式
                                        String[] args = userTask.getId().split("___");
                                        if (args.length > 1) {
                                            Long parent = null;
                                            FlowableRecord record = flowableRecordService.getById(task.getProcessInstanceId());
                                            Map startVariables = JSONUtil.toBean(record.getBusinessValues(), Map.class);
                                            if ("select_user".equals(args[1])) {
                                                String userName = (String) startVariables.get(args[2]);
                                                if (StringUtils.isBlank(userName)) {
                                                    throw new FlowableException("用户信息错误！");
                                                }
                                                Long execUserId = flowableCommonRepository.selectuserName(userName);
                                                parent = execUserId;
                                                completeVariables.put(args[2], execUserId.toString());
                                            } else if ("select_dept".equals(args[1])) {
                                                String deptName = (String) startVariables.get(args[2]);
                                                if (StringUtils.isBlank(deptName)) {
                                                    throw new FlowableException("部门信息错误！");
                                                }
                                                Long leaderUserId = flowableCommonRepository.selectUserDeptLeader(deptName);
                                                if (leaderUserId == null) {
                                                    throw new FlowableException("部门负责人未设置！");
                                                }
                                                parent = leaderUserId;
                                                completeVariables.put(args[2], leaderUserId.toString());
                                            } else {
                                                parent = getLeader(completeVariables, args, parent, startVariables, flowableCommonRepository);
                                            }
                                        }
                                        break;
                                    }
                                } else if (target instanceof ExclusiveGateway) {
                                    List<SequenceFlow> exclusiveGatewayOutgoingFlows = ((ExclusiveGateway) target).getOutgoingFlows();
                                    SequenceFlow sequenceFlow = this.getSequenceFlow(variables, exclusiveGatewayOutgoingFlows);
                                    target = this.getFlowElement(map.values(), sequenceFlow.getTargetRef());
                                    if (handFlow(task, completeVariables, target)) break;
                                } else if (target instanceof InclusiveGateway) {
                                    List<SequenceFlow> inclusiveGatewayOutgoingFlows = ((InclusiveGateway) target).getOutgoingFlows();
                                    SequenceFlow sequenceFlow = this.getSequenceFlow(variables, inclusiveGatewayOutgoingFlows);
                                    target = this.getFlowElement(map.values(), sequenceFlow.getTargetRef());
                                    if (handFlow(task, completeVariables, target)) break;
                                }
                            }
                        }
                    }
                }
            }
        }

        return completeVariables;
    }

    private boolean handFlow(Task task, Map<String, Object> completeVariables, FlowElement target) {
        if (target instanceof UserTask) {
            UserTask userTask = (UserTask) target;
            if (StringUtils.isNotBlank(userTask.getAssignee()) && StringUtils.checkUEL(userTask.getAssignee())) {
                // 填充uel表达式
                String[] args = userTask.getId().split("___");
                if (args.length > 1) {
                    Long parent = null;
                    FlowableRecord record = flowableRecordService.getById(task.getProcessInstanceId());
                    Map startVariables = JSONUtil.toBean(record.getBusinessValues(), Map.class);
                    if ("select_user".equals(args[1])) {
                        String userName = (String) startVariables.get(args[2]);
                        if (StringUtils.isBlank(userName)) {
                            throw new FlowableException("用户信息错误！");
                        }
                        Long execUserId = flowableCommonRepository.selectuserName(userName);
                        parent = execUserId;
                        completeVariables.put(args[2], execUserId.toString());
                    } else if ("select_dept".equals(args[1])) {
                        String deptName = (String) startVariables.get(args[2]);
                        if (StringUtils.isBlank(deptName)) {
                            throw new FlowableException("部门信息错误！");
                        }
                        Long leaderUserId = flowableCommonRepository.selectUserDeptLeader(deptName);
                        if (leaderUserId == null) {
                            throw new FlowableException("部门负责人未设置！");
                        }
                        parent = leaderUserId;
                        completeVariables.put(args[2], leaderUserId.toString());
                    } else {
                        parent = getLeader(completeVariables, args, parent, startVariables, flowableCommonRepository);
                    }
                } else if (userTask.getName().equals("负责人")) {
                        FlowableRecord record = flowableRecordService.getById(task.getProcessInstanceId());
                        Map startVariables = JSONUtil.toBean(record.getBusinessValues(), Map.class);
                        List<SequenceFlow> userTasks = ((UserTask) target).getOutgoingFlows();
                        FlowElement targetNew = null;
                        Long parent = null;
                        if (CollectionUtil.isNotEmpty(userTasks)) {
                            target = userTasks.get(0).getTargetFlowElement();
                            if (target instanceof UserTask) {
                                UserTask userTaskNew = (UserTask) target;
                                args = userTaskNew.getId().split("___");
                                parent = getLeader(completeVariables, args, parent, startVariables, flowableCommonRepository);
                            }

                    }
                }
                return true;
            }
        }
        return false;
    }

    static Long getLeader(Map<String, Object> completeVariables, String[] args, Long parent, Map startVariables, FlowableCommonMapper flowableCommonRepository) {
        if ("matrix".equals(args[1])) {
            String[] matrix = args[2].split("-");
            if (matrix.length != 2) {
                throw new FlowableException("矩阵信息错误！");
            }
            Long leaderUserId = flowableCommonRepository.selectUserMatrixLeader(Long.parseLong(startVariables.get("deptId").toString()), Long.parseLong(matrix[0]), matrix[1]);
            if (leaderUserId == null) {
                leaderUserId = flowableCommonRepository.selectUserMatrixLeaderNew(Long.parseLong(startVariables.get("deptId").toString()), Long.parseLong(matrix[0]), matrix[1]);
                if (leaderUserId == null) {
                    throw new FlowableException("负责人未设置！");
                }
            }
             completeVariables.put(matrix[1], leaderUserId.toString());
            parent = leaderUserId;
        }
        return parent;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeTaskAdmin(TaskVo vo, boolean isActive) {
        String taskId = vo.getTaskId();

        Task task = getTaskNotNull(taskId);
        String currUserId = task.getAssignee();

        currUserId = StringUtils.isNotBlank(currUserId) ? currUserId : "1";

        Map<String, Object> completeVariables = vo.getValues();

        // 激活流程
        if (isActive) {
            runtimeService.activateProcessInstanceById(task.getProcessInstanceId());
        }

        this.addComment(taskId, task.getProcessInstanceId(), currUserId,
                FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey()) ? CommentTypeEnum.CXTJ :
                        CommentTypeEnum.WC, vo.getMessage());

        // 处理抄送
        if (ObjectUtil.isNotEmpty(vo.getCcToVos())) {
            managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(),
                    currUserId, vo.getCcToVos()));

            // 添加流程记录查看权限
            addRecordPower(vo.getCcToVos() != null ? Arrays.asList(vo.getCcToVos()) : null, task.getProcessInstanceId(), flowableRecordPowerService, null);
        }

        if (task.getAssignee() == null || !task.getAssignee().equals(currUserId)) {
            taskService.setAssignee(taskId, currUserId);
        }
        // 判断是否是协办完成还是正常流转
        if (permissionService.isTaskPending(task)) {
            taskService.resolveTask(taskId, completeVariables);
            // 如果当前执行人是任务所有人，直接完成任务
            if (currUserId.equals(task.getOwner())) {
                taskService.complete(taskId, completeVariables);
            }
        } else {
            taskService.complete(taskId, completeVariables);
        }

        // 意见修改
        FlowableRecord record = new FlowableRecord();
        record.setProcessInstanceId(task.getProcessInstanceId());
        record.setProcessContent(vo.getMessage());
        record.setBusinessValues(JSONUtil.toJsonStr(completeVariables.get("processInstanceFormData")));
        flowableRecordService.updateById(record);
    }

    /**
     * 添加记录访问权限
     *
     * @param ccToVos                    抄送列表
     * @param processInstanceId          实例ID
     * @param flowableRecordPowerService 记录Service
     */
    static void addRecordPower(List<CcToVo> ccToVos, String processInstanceId, IFlowableRecordPowerService flowableRecordPowerService, String userId) {
        if (StringUtils.isNotBlank(userId)) {
            FlowableRecordPowerQuery criteria = new FlowableRecordPowerQuery();
            criteria.setUserId(Long.parseLong(userId));
            criteria.setProcessIntaneId(processInstanceId);
            if (flowableRecordPowerService.count(WhereEntityTool.invoke(criteria)) == 0) {
                FlowableRecordPower recordPower = new FlowableRecordPower();
                recordPower.setProcessIntaneId(processInstanceId);
                recordPower.setType(FlowableConstant.TJ);
                recordPower.setUserId(Long.parseLong(userId));
                flowableRecordPowerService.save(recordPower);
            }
        }

        if (CollectionUtil.isNotEmpty(ccToVos)) {
            for (CcToVo cc : ccToVos) {
                if (cc != null && StringUtils.isNotBlank(cc.getUserId())) {
                    FlowableRecordPowerQuery criteria = new FlowableRecordPowerQuery();
                    criteria.setUserId(Long.parseLong(cc.getUserId()));
                    criteria.setProcessIntaneId(processInstanceId);
                    if (flowableRecordPowerService.count(WhereEntityTool.invoke(criteria)) == 0) {
                        FlowableRecordPower recordPower = new FlowableRecordPower();
                        recordPower.setProcessIntaneId(processInstanceId);
                        recordPower.setType(FlowableConstant.CC);
                        recordPower.setUserId(Long.parseLong(cc.getUserId()));
                        flowableRecordPowerService.save(recordPower);
                    }
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTask(String taskId) {
        HistoricTaskInstance task = this.getHistoricTaskInstanceNotNull(taskId);
        if (task.getEndTime() == null) {
            throw new FlowableException("Cannot delete task that is running");
        }
        historyService.deleteHistoricTaskInstance(task.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopProcessInstance(TaskVo vo) {
        String data = Authentication.getAuthenticatedUserId();
        if (StringUtils.isNotBlank(vo.getTaskId())) {
            Long userId = SecurityUtils.getUserId();
            String taskId = vo.getTaskId();
            String processInstanceId = stopProcess(taskId, vo.getMessage(), ApprovalDescriptionEnum.J);

            //添加权限
            FlowableRecordPowerQuery criteria = new FlowableRecordPowerQuery();
            criteria.setUserId(userId);
            criteria.setProcessIntaneId(processInstanceId);
            if (flowableRecordPowerService.count(WhereEntityTool.invoke(criteria)) == 0) {
                FlowableRecordPower recordPower = new FlowableRecordPower();
                recordPower.setProcessIntaneId(processInstanceId);
                recordPower.setType(FlowableConstant.ZZ);
                recordPower.setUserId(userId);
                flowableRecordPowerService.save(recordPower);
            }

            List<Comment> datas = taskService.getProcessInstanceComments(processInstanceId);
            if (CollectionUtil.isNotEmpty(datas)) {
                Collections.reverse(datas);
//                // 本地消息
//                Msg msg = new Msg();
//                msg.setFlowdType("FlowableStop");
//                msg.setMsgTitle("流程终止");
//                msg.setMsgContent("您有一条流程终止，请及时查看！");
//                msg.setUserId(Long.parseLong(datas.get(0).getUserId()));
//                msg.setFlowdId(processInstanceId);
//                msgService.create(msg);
//                // 推送消息
//                usersGetuiService.pushMsg(Collections.singleton(msg.getUserId()), msg.getMsgTitle(), msg.getMsgContent());
            }
        } else if (StringUtils.isNotBlank(vo.getProcessInstanceId())) {
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).taskCategory(FlowableConstant.CATEGORY_TODO).listPage(0, 1);
            String taskId = taskList.get(0).getId();
            String userId = SecurityUtils.getUserId().toString();
            this.addComment(taskId, vo.getProcessInstanceId(), userId,
                    CommentTypeEnum.ZZ, vo.getMessage());
            stopProcess(taskId, vo.getMessage(), ApprovalDescriptionEnum.T, userId);
        } else {
            Authentication.setAuthenticatedUserId(data);
            throw new FlowableException("id 不能为空");
        }
        flowableRecordPowerService.updateFlowStatus(vo.getProcessInstanceId(), ApprovalDescriptionEnum.T.getValue());
        Authentication.setAuthenticatedUserId(data);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopProcessInstance(TaskVo vo, Long userId) {
        if (StringUtils.isNotBlank(vo.getProcessInstanceId())) {
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).taskCategory(FlowableConstant.CATEGORY_TODO).listPage(0, 1);
            String taskId = taskList.get(0).getId();
            stopProcess(taskId, vo.getMessage(), ApprovalDescriptionEnum.T, userId.toString(), vo.getProcessInstanceId());
        } else {
            throw new FlowableException("id 不能为空");
        }
    }

    private String stopProcess(String taskId, String msg, ApprovalDescriptionEnum status) {
        String userId = SecurityUtils.getUserId().toString();
        ProcessInstance processInstance = permissionService.validateStopProcessInstancePermissionOnTask(taskId, userId);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        // 终止
        FlowableRecord record = new FlowableRecord();
        record.setProcessInstanceId(processInstance.getProcessInstanceId());
        record.setStatus(status.getValue());
        record.setExecutorId(Long.parseLong(userId));
        record.setProcessContent(msg);
        flowableRecordService.updateById(record);

        if (bpmnModel != null) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (endNodes != null && endNodes.size() > 0) {
                // 2024-02-19 无需添加终止意见
                //this.addComment(taskId, processInstance.getProcessInstanceId(), userId, CommentTypeEnum.ZZ, msg);
                String endId = endNodes.get(0).getId();
                List<Execution> executions =
                        runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                log.info("执行id长度===={}", executionIds.size());
                executions.forEach(execution -> executionIds.add(execution.getId()));
                try {
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                } catch (Exception e) {
                    executions =
                            runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                    log.info("异常后执行id长度===={}", executionIds.size());
                    executions.forEach(execution -> executionIds.add(execution.getId()));
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                }

            }
        }

        return processInstance.getProcessInstanceId();
    }


    private String stopProcess(String taskId, String msg, ApprovalDescriptionEnum status, String userId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new FlowableObjectNotFoundException("Task with id: " + taskId + " does not exist");
        }
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        if (processInstance == null) {
            throw new FlowableObjectNotFoundException("ProcessInstance with id: " + task.getProcessInstanceId() + " " + "does not exist");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        // 终止
        FlowableRecord record = new FlowableRecord();
        record.setProcessInstanceId(processInstance.getProcessInstanceId());
        record.setStatus(status.getValue());
        record.setExecutorId(Long.parseLong(userId));
        record.setProcessContent(msg);
        flowableRecordService.updateById(record);

        if (bpmnModel != null) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (endNodes != null && endNodes.size() > 0) {
                // 2024-02-19 无需添加终止意见
                //this.addComment(taskId, processInstance.getProcessInstanceId(), userId, CommentTypeEnum.ZZ, msg);
                String endId = endNodes.get(0).getId();
                List<Execution> executions =
                        runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                log.info("执行id长度===={}", executionIds.size());
                executions.forEach(execution -> executionIds.add(execution.getId()));
                try {
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                } catch (Exception e) {
                    executions =
                            runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                    log.info("异常后执行id长度===={}", executionIds.size());
                    executions.forEach(execution -> executionIds.add(execution.getId()));
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                }

            }
        }

        return processInstance.getProcessInstanceId();
    }


    private String stopProcess(String taskId, String msg, ApprovalDescriptionEnum status, String userId, String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        // 终止
        FlowableRecord record = new FlowableRecord();
        record.setProcessInstanceId(processInstance.getProcessInstanceId());
        record.setStatus(status.getValue());
        record.setExecutorId(Long.parseLong(userId));
        record.setProcessContent(msg);
        flowableRecordService.updateById(record);

        if (bpmnModel != null) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (endNodes != null && endNodes.size() > 0) {
                // 2024-02-19 无需添加终止意见
                //this.addComment(taskId, processInstance.getProcessInstanceId(), userId, CommentTypeEnum.ZZ, msg);
                String endId = endNodes.get(0).getId();
                List<Execution> executions =
                        runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                log.error("执行id===={}", executionIds.toString());
                try {
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                } catch (Exception e) {
                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException ex) {
                        throw new RuntimeException(ex);
                    }
                    executions =
                            runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                    executionIds.clear();
                    executions.forEach(execution -> executionIds.add(execution.getId()));
                    log.error("异常后执行id===={}", executionIds.toString());
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                }
            }
        }

        return processInstance.getProcessInstanceId();
    }

    @Override
    public List<FlowNodeResponse> getBackNodes(String taskId) {
        TaskEntity taskEntity = (TaskEntity) permissionService.validateExcutePermissionOnTask(taskId,
                SecurityUtils.getUserId().toString());
        permissionService.validateTaskHasButtonPermission(taskEntity, ButtonsEnum.BACK);
        String processInstanceId = taskEntity.getProcessInstanceId();
        String currActId = taskEntity.getTaskDefinitionKey();
        String processDefinitionId = taskEntity.getProcessDefinitionId();
        Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
        FlowNode currentFlowElement = (FlowNode) process.getFlowElement(currActId, true);
        List<ActivityInstance> activitys =
                runtimeService.createActivityInstanceQuery().processInstanceId(processInstanceId).finished().orderByActivityInstanceStartTime().asc().list();
        List<String> activityIds =
                activitys.stream().filter(activity -> activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_TASK_USER)).map(ActivityInstance::getActivityId).filter(activityId -> !activityId.equals(currActId)).distinct().collect(Collectors.toList());
        List<FlowNodeResponse> result = new ArrayList<>();
        for (String activityId : activityIds) {
            FlowNode toBackFlowElement = (FlowNode) process.getFlowElement(activityId, true);
            if (FlowableUtils.isReachable(process, toBackFlowElement, currentFlowElement)) {
                FlowNodeResponse vo = new FlowNodeResponse();
                vo.setNodeId(activityId);
                vo.setNodeName(toBackFlowElement.getName());
                result.add(vo);
            }
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backTask(TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = permissionService.validateExcutePermissionOnTask(taskId, userId);
        permissionService.validateTaskHasButtonPermission(task, ButtonsEnum.BACK);
        String backSysMessage = "退回到" + vo.getActivityName() + "。";
        Map<String, Object> variables = new HashMap<>();
        variables.put("activityId", vo.getActivityId());
        runtimeService.setVariables(task.getProcessInstanceId(), variables);
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.TH,
                backSysMessage + vo.getMessage());
        addRecordPower(null, task.getProcessInstanceId(), flowableRecordPowerService, userId);
        // 处理抄送
        if (ObjectUtil.isNotEmpty(vo.getCcToVos())) {
            managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(), vo.getCurrentId()
                    , vo.getCcToVos()));

            // 添加流程记录查看权限

            addRecordPower(vo.getCcToVos() != null ? Arrays.asList(vo.getCcToVos()) : null, task.getProcessInstanceId(), flowableRecordPowerService, null);
        }
        String targetRealActivityId = managementService.executeCommand(new BackUserTaskCmd(runtimeService,
                vo.getTaskId(), vo.getActivityId()));
        // 退回发起者处理,退回到发起者,默认设置任务执行人为发起者
        if (FlowableConstant.INITIATOR.equals(targetRealActivityId)) {
            String initiator =
                    runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult().getStartUserId();
            List<Task> newTasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            for (Task newTask : newTasks) {
                // 约定：发起者节点为 __initiator__
                if (FlowableConstant.INITIATOR.equals(newTask.getTaskDefinitionKey())) {
                    if (ObjectUtil.isEmpty(newTask.getAssignee())) {
                        taskService.setAssignee(newTask.getId(), initiator);
                    }
                }
            }
        }
    }

    @Override
    public void withdraw(TaskVo vo) {
        String userId = SecurityUtils.getUserId().toString();
        String nickName = SecurityUtils.getNickName();
        //获取当前任务
        List<Task> tList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).list();
        if (CollectionUtil.isEmpty(tList)) {
            throw new FlowableException("E01:当前任务不存在");
        }
        Task t = tList.get(0);
        //获取上一个任务节点
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(vo.getProcessInstanceId()) // 替换为你的流程实例ID
                .finished() // 只查询已经完成的任务
                .orderByHistoricTaskInstanceEndTime().desc(); // 按照结束时间降序排列
        List<HistoricTaskInstance> historicTaskInstances = query.list();
        if (CollectionUtil.isEmpty(historicTaskInstances)) {
            throw new FlowableException("E01:当前任务不能撤回");
        }
        HistoricTaskInstance  previous = null;
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {

            if (!historicTaskInstance.getName().equals(t.getName()) && !"MI_END".equals(historicTaskInstance.getDeleteReason())) {
                previous = historicTaskInstance;
                break;
            }
        }

        if (previous == null || previous.getAssignee() == null || (!previous.getAssignee().equals(userId) &&  !SecurityUtils.isAdmin(Long.parseLong(userId)))) {
            throw new FlowableException("E01:当前任务不能撤回");
        }
        String backSysMessage = nickName+":撤回";
        this.addComment(t.getId(), vo.getProcessInstanceId(), userId, CommentTypeEnum.CH,
                backSysMessage + (StringUtils.isNotEmpty(vo.getMessage())? vo.getMessage():""));

        managementService.executeCommand(new BackTaskCmd(runtimeService,
                t.getId(), previous.getTaskDefinitionKey()));

    }




    @Override
    @PlugAnn(groupClass = FlowableBack.Approval.class)
    @Transactional(rollbackFor = Exception.class)
    public void refuseTask(@PlugParam TaskVo vo) {
        String taskId = vo.getTaskId();
        String userId = SecurityUtils.getUserId().toString();
        Task task = permissionService.validateExcutePermissionOnTask(taskId, userId);
        permissionService.validateTaskHasButtonPermission(task, ButtonsEnum.BACK);
        String backSysMessage = "拒绝到" + vo.getActivityName() + "。";
        Map<String, Object> variables = new HashMap<>();
        variables.put("activityId", vo.getActivityId());
        runtimeService.setVariables(task.getProcessInstanceId(), variables);
        this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.TH,
                backSysMessage + vo.getMessage());
        addRecordPower(null, task.getProcessInstanceId(), flowableRecordPowerService, userId);
        // 处理抄送
        if (ObjectUtil.isNotEmpty(vo.getCcToVos())) {
            managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(), vo.getCurrentId()
                    , vo.getCcToVos()));

            // 添加流程记录查看权限

            addRecordPower(vo.getCcToVos() != null ? Arrays.asList(vo.getCcToVos()) : null, task.getProcessInstanceId(), flowableRecordPowerService, null);
        }
        String targetRealActivityId = managementService.executeCommand(new BackUserTaskCmd(runtimeService,
                vo.getTaskId(), vo.getActivityId()));
        // 退回发起者处理,退回到发起者,默认设置任务执行人为发起者
        if (FlowableConstant.INITIATOR.equals(targetRealActivityId)) {
            String initiator =
                    runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult().getStartUserId();
            List<Task> newTasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            for (Task newTask : newTasks) {
                // 约定：发起者节点为 __initiator__
                if (FlowableConstant.INITIATOR.equals(newTask.getTaskDefinitionKey())) {
                    if (ObjectUtil.isEmpty(newTask.getAssignee())) {
                        taskService.setAssignee(newTask.getId(), initiator);
                    }
                }
            }
        }
    }

    private void fillPermissionInformation(TaskResponse taskResponse, TaskInfo task, String userId) {
        verifyProcessInstanceStartUser(taskResponse, task);
        List<HistoricIdentityLink> taskIdentityLinks = historyService.getHistoricIdentityLinksForTask(task.getId());
        verifyCandidateGroups(taskResponse, userId, taskIdentityLinks);
        verifyCandidateUsers(taskResponse, userId, taskIdentityLinks);
    }

    private void verifyProcessInstanceStartUser(TaskResponse TaskDto, TaskInfo task) {
        if (task.getProcessInstanceId() != null) {
            HistoricProcessInstance historicProcessInstance =
                    historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            if (historicProcessInstance != null && StringUtils.isNotEmpty(historicProcessInstance.getStartUserId())) {
                TaskDto.setProcessInstanceStartUserId(historicProcessInstance.getStartUserId());
                BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
                FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
                if (flowElement instanceof UserTask) {
                    UserTask userTask = (UserTask) flowElement;
                    List<ExtensionElement> extensionElements = userTask.getExtensionElements().get("initiator-can" +
                            "-complete");
                    if (extensionElements != null && !extensionElements.isEmpty()) {
                        String value = extensionElements.get(0).getElementText();
                        if (StringUtils.isNotEmpty(value)) {
                            TaskDto.setInitiatorCanCompleteTask(value);
                        }
                    }
                }
            }
        }
    }

    private void verifyCandidateGroups(TaskResponse taskResponse, String userId,
                                       List<HistoricIdentityLink> taskIdentityLinks) {
        List<Group> userGroups = identityService.createGroupQuery().groupMember(userId).list();
        taskResponse.setMemberOfCandidateGroup(String.valueOf(userGroupsMatchTaskCandidateGroups(userGroups,
                taskIdentityLinks)));
    }

    private boolean userGroupsMatchTaskCandidateGroups(List<Group> userGroups,
                                                       List<HistoricIdentityLink> taskIdentityLinks) {
        for (Group group : userGroups) {
            for (HistoricIdentityLink identityLink : taskIdentityLinks) {
                if (identityLink.getGroupId() != null && identityLink.getType().equals(IdentityLinkType.CANDIDATE) && group.getId().equals(identityLink.getGroupId())) {
                    return true;
                }
            }
        }
        return false;
    }

    private void verifyCandidateUsers(TaskResponse taskResponse, String userId,
                                      List<HistoricIdentityLink> taskIdentityLinks) {
        taskResponse.setMemberOfCandidateUsers(String.valueOf(currentUserMatchesTaskCandidateUsers(userId,
                taskIdentityLinks)));
    }

    private boolean currentUserMatchesTaskCandidateUsers(String userId, List<HistoricIdentityLink> taskIdentityLinks) {
        for (HistoricIdentityLink identityLink : taskIdentityLinks) {
            if (identityLink.getUserId() != null && identityLink.getType().equals(IdentityLinkType.CANDIDATE) && identityLink.getUserId().equals(userId)) {
                return true;
            }
        }
        return false;
    }

    private String getUserName(String userId) {
        if (StringUtils.isBlank(userId)) {
            return null;
        }
        User user = identityService.createUserQuery().userId(userId).singleResult();
        if (user != null) {
            return user.getFirstName();
        }
        return null;
    }

    private List<String> getInvolvedUsers(String taskId) {
        List<HistoricIdentityLink> idLinks = historyService.getHistoricIdentityLinksForTask(taskId);
        List<String> result = new ArrayList<>(idLinks.size());

        for (HistoricIdentityLink link : idLinks) {
            // Only include users and non-assignee links
            if (link.getUserId() != null && !IdentityLinkType.ASSIGNEE.equals(link.getType())) {
                result.add(link.getUserId());
            }
        }
        return result;
    }

    private void populateAssignee(TaskInfo task, TaskResponse rep) {
        if (task.getAssignee() != null) {
            rep.setAssignee(task.getAssignee());
        }
    }

    @Override
    public Task getTaskNotNull(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new FlowableObjectNotFoundException("Task with id: " + taskId + " does not exist");
        }
        return task;
    }

    @Override
    public HistoricTaskInstance getHistoricTaskInstanceNotNull(String taskId) {
        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new FlowableObjectNotFoundException("Task with id: " + taskId + " does not exist");
        }
        return task;
    }

    @Override
    public void addComment(String taskId, String processInstanceId, String userId, CommentTypeEnum type,
                           String message) {
        Authentication.setAuthenticatedUserId(userId);
        type = type == null ? CommentTypeEnum.ZF : type;
        message = (message == null || message.length() == 0) ? type.getName() : message;
        taskService.addComment(taskId, processInstanceId, type.toString(), message);
    }

    public void addCommentZF(String taskId, String processInstanceId, String userId, CommentTypeEnum type,
                           String message) {
        Authentication.setAuthenticatedUserId(userId);
        type = type == null ? CommentTypeEnum.ZF : type;
        message = (message == null || message.length() == 0) ? type.getName() : message;
        managementService.executeCommand(new AddCommentCmdNew(taskId, processInstanceId, type.toString(), message));
    }

    @Override
    public List<Comment> getComments(String taskId, String processInstanceId, String type, String userId) {
        List<Comment> comments;
        if (type == null || type.length() == 0) {
            // 以taskId为优先
            if (taskId != null && taskId.length() > 0) {
                comments = taskService.getTaskComments(taskId);
            } else if (processInstanceId != null && processInstanceId.length() > 0) {
                comments = taskService.getProcessInstanceComments(processInstanceId);
            } else {
                throw new FlowableIllegalArgumentException("taskId processInstanceId type are all empty");
            }
        } else {
            // 以taskId为优先
            if (taskId != null && taskId.length() > 0) {
                comments = taskService.getTaskComments(taskId, type);
            } else if (processInstanceId != null && processInstanceId.length() > 0) {
                comments = taskService.getProcessInstanceComments(processInstanceId, type);
            } else {
                comments = taskService.getCommentsByType(type);
            }
        }
        if (userId != null && userId.length() > 0 && comments != null && comments.size() > 0) {
            comments =
                    comments.stream().filter(comment -> userId.equals(comment.getUserId())).collect(Collectors.toList());
        }
        return comments;
    }

    private void validateIdentityLinkArguments(String identityId, String identityType) {
        if (identityId == null || identityId.length() == 0) {
            throw new FlowableIllegalArgumentException("identityId is null");
        }
        if (!FlowableConstant.IDENTITY_ALL.equals(identityType) && !FlowableConstant.IDENTITY_GROUP.equals(identityType) && !FlowableConstant.IDENTITY_USER.equals(identityType)) {
            throw new FlowableIllegalArgumentException("type must be group or user");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTaskIdentityLink(IdentityVo vo) {
        Task task = getTaskNotNull(vo.getTaskId());
        validateIdentityLinkArguments(vo.getIdentityId(), vo.getIdentityType());
        if (FlowableConstant.IDENTITY_ALL.equals(vo.getIdentityType()) || FlowableConstant.IDENTITY_GROUP.equals(vo.getIdentityType())) {
            taskService.addGroupIdentityLink(task.getId(), vo.getIdentityId(),
                    IdentityLinkType.CANDIDATE);
        } else if (FlowableConstant.IDENTITY_USER.equals(vo.getIdentityType())) {
            taskService.addUserIdentityLink(task.getId(), vo.getIdentityId(),
                    IdentityLinkType.CANDIDATE);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTaskIdentityLink(String taskId, String identityId, String identityType) {
        Task task = getTaskNotNull(taskId);
        validateIdentityLinkArguments(identityId, identityType);
        if (FlowableConstant.IDENTITY_ALL.equals(identityType) || FlowableConstant.IDENTITY_GROUP.equals(identityType)) {
            taskService.deleteGroupIdentityLink(task.getId(), identityId, IdentityLinkType.CANDIDATE);
        } else if (FlowableConstant.IDENTITY_USER.equals(identityType)) {
            taskService.deleteUserIdentityLink(task.getId(), identityId, IdentityLinkType.CANDIDATE);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void readTask(TaskVo vo) {
        String[] taskIds = vo.getTaskIds();
        if (taskIds == null || taskIds.length == 0) {
            throw new FlowableException("taskIds is null or empty");
        }
        String userId = SecurityUtils.getUserId().toString();
        for (String taskId : taskIds) {
            managementService.executeCommand(new CompleteTaskReadCmd(taskId, userId));
        }
    }

    @Override
    public void urging(TaskVo vo) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).taskCategory(FlowableConstant.CATEGORY_TODO).listPage(0, 1);
        if (CollectionUtil.isNotEmpty(taskList) && StringUtils.isNotBlank(taskList.get(0).getAssignee())) {
            if (taskList.get(0).getAssignee().equals(SecurityUtils.getUserId().toString())) {
                throw new FlowableException("任务暂未人认领！");
            }
//            MsgQueryCriteria criteria = new MsgQueryCriteria();
//            criteria.setFlowdId(vo.getProcessInstanceId());
//            criteria.setFlowdType("FlowableUrging");
//            List<Timestamp> timestampList = new ArrayList<>();
//            Date date = new Date();
//            timestampList.add(Timestamp.from(DateUtil.getStartOfDay(date).atZone(ZoneId.systemDefault()).toInstant()));
//            timestampList.add(Timestamp.from(DateUtil.getEndOfDay(date).atZone(ZoneId.systemDefault()).toInstant()));
//            criteria.setCreateTime(timestampList);
//            if (msgService.count(criteria) > 0) {
//                throw new FlowableException("一天只能催办一次！");
//            }

//            // 本地消息
//            Msg msg = new Msg();
//            msg.setFlowdType("FlowableUrging");
//            msg.setMsgTitle("流程催办");
//            msg.setMsgContent("您有一条流程催办，请及时查看！");
//            msg.setUserId(Long.parseLong(taskList.get(0).getAssignee()));
//            msg.setFlowdId(vo.getProcessInstanceId());
//            msgService.create(msg);
//            // 推送消息
//            usersGetuiService.pushMsg(Collections.singleton(msg.getUserId()), msg.getMsgTitle(), msg.getMsgContent());
        }
    }

    private SequenceFlow getSequenceFlow(Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows) {
        List<SequenceFlow> sequenceFlows = this.getSequenceFlows(variableMap, outgoingFlows);
        if (null != sequenceFlows && sequenceFlows.size() > 0) {
            return sequenceFlows.get(0);
        }

        return outgoingFlows.get(0);
    }

    /**
     * 根据传入的变量，计算出表达式成立的SequenceFlow连线集合
     *
     * @param variableMap   变量map
     * @param outgoingFlows 输出流
     * @return SequenceFlow连线集合
     */
    private List<SequenceFlow> getSequenceFlows(Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows) {
        List<SequenceFlow> sequenceFlows;
        sequenceFlows = outgoingFlows.stream().filter(item -> {
            try {
                return this.getElValue(null, item.getConditionExpression(), variableMap);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }).collect(Collectors.toList());
        return sequenceFlows;
    }

    public boolean getElValue(String processInstanceId, String exp, Map<String, Object> variableMap) {
        if (StringUtils.isBlank(exp)) {
            return true;
        }
        boolean flag = managementService.executeCommand(new ExpressionCmd(processEngine, processInstanceId, exp, variableMap));
        return flag;
    }

    private FlowElement getFlowElement(Collection<FlowElement> flowElements, String targetRef) {
        return flowElements.stream().filter(flowElement -> targetRef.equals(flowElement.getId())).findFirst().orElse(null);
    }
}
